iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

哈囉大家好!今天我們要來玩點有趣的東西了 —— 我們要開始打造我們的網頁骨架啦!想像一下,我們今天要給我們的 Nuxt3 專案穿上漂亮的衣服,讓它不再只是一個空蕩蕩的框架。準備好了嗎?讓我們開始吧!

先來看看我們的目標

首先,讓我們來看看我們要做出來的頁面吧:

  1. 聯繫我們頁面:
    https://ithelp.ithome.com.tw/upload/images/20241004/20159686rwAHn5G0v1.png

  2. 食物計算機頁面:
    https://ithelp.ithome.com.tw/upload/images/20241004/201596866yJAOVpWxa.pnghttps://ithelp.ithome.com.tw/upload/images/20241004/20159686xYndNzmg8V.png

是不是看起來很酷?接下來我們會一步一步把這些頁面實現出來 (但不是今天)!

開始動手做吧!

1. 整理 app.vue

首先,我們要整理一下 app.vue。這個檔案就像是我們網站的大骨架,所有的頁面都會包在這裡面。

<script setup>
import axios from 'axios'

// 建立一個 Axios 實例,就像是給我們的 API 請求一個專屬的小幫手
const api = axios.create({
  baseURL: 'https://two024it-test-app.onrender.com'// 設定基礎 URL
})

const list = ref([])

// 取得資料的函式
async function fetchData() {
  try {
    const response = await api.get('/freshfoods/')// 取得食物列表
    console.log(response.data)// 在控制台印出回應資料
    list.value = response.data
  } catch (error) {
    console.error('哎呀,出錯了:', error)// 錯誤處理
  }
}

onMounted(() => {
  fetchData()
})
</script>

<template>
  <div class="relative flex min-h-screen w-full flex-col">
<!-- 導航欄 -->
    <header class="z-20">
<!-- <Header /> -->
    </header>

<!-- 頁面內容 -->
    <main class="page-wrapper">
      <NuxtPage />
    </main>
  </div>
</template>

<style scoped>
.page-wrapper {
  @apply mx-auto flex w-full max-w-[1200px] grow px-3 pb-5 pt-[52px] lg:px-5 lg:pt-[64px];
  align-items: start;
}
</style>

這裡我們用了 <NuxtPage /> 組件,它就像是一個神奇的佔位符,Nuxt 會自動把我們的頁面內容放在這裡。

2. 建立首頁

接下來,我們要在 pages 資料夾裡新增一個 index.vue 檔案。這就是我們的首頁啦!

<script setup></script>

<template>
  <div>index</div>
</template>

<style scoped></style>

當你執行專案並進入首頁時,你會看到 "index" 這個字。這是因為 <NuxtPage /> 幫我們把 pages/index.vue 的內容放到那裡了。是不是很神奇?
https://ithelp.ithome.com.tw/upload/images/20241004/20159686JZuLvNIisZ.png

3. 新增其他頁面

接著,我們要在 pages 資料夾裡新增所有需要的頁面。我會新增以下頁面:

  • index.vue(首頁,已存在)
  • calculator.vue(食物計算機)
  • food.vue(營養指南)
  • bird.vue(百科全書)
  • hospital.vue(醫護站)
  • connect.vue(聯繫我們)

每個檔案可以使用以下基本結構:

<template>
  <div>
    <h1>頁面標題</h1>
    <!-- 在這裡添加頁面內容 -->
  </div>
</template>

<script setup>
// 這裡可以添加需要的 JavaScript 邏輯
</script>

<style scoped>
/* 這裡可以添加頁面專屬的樣式 */
</style>

4. 設定網站基礎配色

為了讓我們的網站看起來更漂亮,我們要設定一些基礎的顏色。打開 tailwind.config.ts,加入以下程式碼:

extend: {
  colors: {
    blue1: '#e9f1fe',
    blue2: '#c4d7ed',
    blue3: '#abc8e2',
    blue4: '#375d81',
    blue5: '#183152',
    red1: '#ffaeaa',
    red2: '#ed6f69',
    bg: '#fff6ea'
  }
}

這樣我們就可以用像 text-blue3 這樣的 class 來使用我們自定義的顏色了!超方便的吧?
https://ithelp.ithome.com.tw/upload/images/20241004/20159686FifMXEgU2X.png

5. 安裝和使用 Nuxt Icon

  1. 安裝圖示套件:

    • 開啟終端機,在專案根目錄執行以下指令安裝 Nuxt Icon 模組:

      npm install -D @nuxt/icon
      
  2. 設定 Nuxt:

    • 打開 nuxt.config.ts 檔案。

    • modules 陣列中添加 nuxt-icon

      export default defineNuxtConfig({
        devtools: { enabled: true },
        modules: ['@nuxtjs/tailwindcss', '@nuxt-icon']
      })
      
  3. 使用圖示:

    • 前往 Icones 網站尋找需要的圖示。

    • 複製您選擇的圖示名稱。
      https://ithelp.ithome.com.tw/upload/images/20241004/20159686xI1BDvJT4b.png

    • 在頁面中使用以下格式插入圖示:

      <Icon name="ph:hand-tap" size="20"></Icon>
      
    • name 屬性中填入您剛剛複製的圖示名稱。

    • size 屬性用來控制圖示大小。

    注意:這邊如果貼在 Header.vue 測試會看不到哦,因為我們 app.vueHeader 元件是註解的狀態 ><

  4. 實用提示:

    • 推薦安裝 VS Code 擴充套件:Iconify IntelliSense
      https://ithelp.ithome.com.tw/upload/images/20241004/20159686d4uvYZPdFR.png

    • 這個擴充套件可以將程式碼中的圖示名稱轉換為可視化的圖示,方便預覽和選擇。
      https://ithelp.ithome.com.tw/upload/images/20241004/201596861xBSMcLCUf.png

完成以上步驟後,您就可以在專案中使用漂亮的圖示了!

6. 建立元件 components 資料夾

  1. 在專案根目錄新增一個 components 資料夾。
    這個資料夾將用來存放所有的 Vue 元件。

  2. components 資料夾內新增一個 Header.vue 檔案。
    這個檔案將包含我們的導航欄元件程式碼。

7. 建立資源 assets 資料夾

  1. 在專案根目錄新增一個 assets 資料夾。
    這個資料夾用於存放靜態資源,如圖片、樣式表等。
  2. assets 資料夾內新增一個 images 資料夾。
    這個資料夾將專門用來存放圖片檔案。
  3. 將 LOGO 圖片放入 assets/images 資料夾中。
    https://ithelp.ithome.com.tw/upload/images/20241004/201596860YI7cnLsxd.png

8. 建立 建立全局樣式文件

在這個步驟中,我們將創建一個全局的 CSS 文件,用於定義整個應用程序的基本樣式和一些通用的 CSS 類。

  1. 在專案根目錄下創建 assets/css/main.css 文件。

  2. 將以下內容複製到 main.css 文件中:

    @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&display=swap');
    
    :root {
      --color-blue1: #e9f1fe;
      --color-blue2: #c4d7ed;
      --color-blue3: #abc8e2;
      --color-blue4: #375d81;
      --color-blue5: #183152;
    
      --color-red1: #ffaeaa;
      --color-red2: #ed6f69;
    
      --color-bg: #fff6ea;
    }
    
    body {
      font-family: 'Noto Sans TC', sans-serif;
      letter-spacing: 1.25px;
      background: var(--color-bg);
    }
    
    /* 消樣式 */
    input:focus-visible {
      outline: none;
    }
    
    textarea:focus-visible {
      outline: none;
    }
    
    select:focus-visible {
      outline: none;
    }
    
    /* 圖片通用 */
    .pic-auto {
      width: 100%;
      height: 100%;
      object-fit: cover;
      vertical-align: middle;
    }
    
    /* 限制最大寬度 !> 1200px, 搭配 px-5 使用 */
    .page-max-w {
      @apply mx-auto w-full max-w-[1200px];
    }
    
    /* hover 效果 */
    .hover-auto {
      @apply transform duration-200;
    }
    

    讓我們來解析這個 CSS 文件的各個部分:

    1. 字體導入:
      • 使用 Google Fonts 導入 Noto Sans TC 字體,這是一種適合中文顯示的sans-serif字體。
    2. CSS 變數定義:
      • :root 選擇器中定義了一系列顏色變數,包括不同深淺的藍色、紅色和背景色。
      • 使用 CSS 變數可以讓我們在整個應用中方便地管理和修改顏色。
    3. 全局樣式:
      • body 元素設置了字體、字間距和背景色。
    4. 表單元素樣式重置:
      • 移除了 inputtextareaselect 元素在獲得焦點時的默認輪廓。
    5. 通用圖片樣式:
      • .pic-auto 類提供了一種便捷的方式來設置圖片的寬高和適應方式。
    6. 頁面最大寬度限制:
      • .page-max-w 類用於限制內容的最大寬度,通常與 padding 一起使用來創建響應式佈局。
    7. Hover 效果:
      • .hover-auto 類為元素添加了過渡效果,可用於創建平滑的懸停動畫。
  3. 要在專案中使用這個 CSS 文件,需要在 nuxt.config.ts 中進行配置:

    export default defineNuxtConfig({
      css: ['~/assets/css/main.css'],
    // 其他配置...
    })
    

    這樣,main.css 中定義的樣式就會被應用到整個專案中。您可以在元件中直接使用這些 CSS 類和變數,例如:

    <template>
      <div class="page-max-w">
        <img src="..." alt="..." class="pic-auto hover-auto" />
      </div>
    </template>
    
    <style scoped>
    .custom-element {
      color: var(--color-blue4);
    }
    </style>
    

    通過這種方式,我們可以確保整個應用程序有一致的樣式,同時也方便了樣式的管理和修改。

9. 查看設計稿

在開始編寫程式碼之前,請仔細查看導航欄的設計稿:

  • 左側是 LOGO
  • 右側是導航選項
    https://ithelp.ithome.com.tw/upload/images/20241004/20159686HxVuW3xe6F.pnghttps://ithelp.ithome.com.tw/upload/images/20241004/20159686eNQ7XI4uib.png
    這個設計將指導我們如何構建 Header.vue 元件的結構和樣式。
    完成這些步驟後,我們就準備好開始製作導航欄元件了。接下來,我們將開始編寫 Header.vue 的內容,實現設計稿中的導航欄功能。

導航欄詳細製作步驟

1. 建立基本結構

首先,我們要在 components 資料夾裡新增一個 Header.vue 檔案。這個檔案會包含我們導航欄的所有程式碼。

<script setup lang="ts">
// 這裡放 JavaScript 程式碼
</script>

<template>
<!-- 這裡放 HTML 結構 -->
</template>

<style scoped>
/* 這裡放 CSS 樣式 */
</style>

這是 Vue 單文件組件的基本結構。<script setup> 區塊用於 JavaScript 邏輯,<template> 用於 HTML 結構,<style scoped> 用於 CSS 樣式。

2. 製作 Logo

我們先來放置網站的 Logo:

<template>
  <nuxt-link to="/" class="logo fixed left-5 top-2 w-[32px] lg:w-[40px]">
    <img src="~/assets/images/logo.svg" alt="logo" class="pic-auto" />
  </nuxt-link>
</template>

這段程式碼做了什麼呢?

  • nuxt-link 是 Nuxt 提供的特殊連結元件,這裡我們用它來連到首頁 "/"
  • class 裡的內容是使用 Tailwind CSS 來設定樣式,例如 fixed 表示固定位置,left-5top-2 設定位置,w-[32px] 設定寬度。
  • img 標籤用來顯示 Logo 圖片:
    • src="~/assets/images/logo.svg" 指定了 Logo 圖片的路徑。~ 是 Nuxt.js 中的特殊符號,代表專案的根目錄。這表示圖片位於專案的 assets/images 資料夾中。

3. 製作選單按鈕

接下來,我們來做選單按鈕:

<script setup lang="ts">
const route = useRoute()
const menuOpen = ref(false)

const toggleMenu = () => {
  menuOpen.value = !menuOpen.value
}
</script>

<template>
  <div class="relative">
    <button v-show="!menuOpen" @click="toggleMenu" class="nav-btn">
      <Icon name="ph:hand-tap" size="20"></Icon>

      <div v-if="route.path === '/'">首頁</div>
      <div v-else-if="route.path === '/calculator'">食物計算機</div>
      <div v-else-if="route.path === '/food'">營養指南</div>
      <div v-else-if="route.path === '/bird'">百科全書</div>
      <div v-else-if="route.path === '/hospital'">醫護站</div>
      <div v-else="route.path === '/connect'">聯繫我們</div>
    </button>
  </div>
</template>

<style scoped>
.nav-btn {
  @apply bg-bg text-blue4 border-blue4 fixed right-0 top-0 z-20 flex w-[140px] items-center justify-center gap-[6px] rounded-bl-[6px] border-b border-l bg-opacity-20 p-[10px] font-bold backdrop-blur-sm;
}
</style>

讓我解釋一下這段程式碼:

  1. const route = useRoute() 讓我們可以知道現在在哪個頁面。
  2. menuOpen 是一個變數,用來記錄選單是開啟還是關閉的。
  3. toggleMenu 函式用來切換選單的開關狀態。
  4. template 中,我們用 v-show="!menuOpen" 來控制按鈕的顯示。
  5. @click="toggleMenu" 表示當按鈕被點擊時,會呼叫 toggleMenu 函式。
  6. 我們用一系列的 v-ifv-else-if 來根據不同的頁面路徑顯示不同的文字。
  7. Icon 元件是用來顯示一個小圖示。

4. 製作選單內容

現在來製作選單的主要內容:

<template>
  <div class="relative">
    <!-- 前面的按鈕程式碼 -->

    <div v-show="menuOpen" class="nav-menu">
      <button class="nav-menu-item" @click="toggleMenu">CLOSE</button>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/">首頁</nuxt-link>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/calculator">食物計算機</nuxt-link>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/food">營養指南</nuxt-link>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/bird">百科全書</nuxt-link>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/hospital">醫護站</nuxt-link>
      <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/connect">聯繫我們</nuxt-link>
    </div>
  </div>
</template>

<style scoped>
.nav-menu {
  @apply text-blue4 border-blue4 fixed right-0 top-0 z-20 flex w-[140px] flex-col items-center justify-center rounded-bl-[6px] border-b border-l bg-opacity-20 font-bold backdrop-blur-sm;
}

.nav-menu-item {
  @apply flex w-full items-center justify-center p-[10px];
}
</style>

這段程式碼做了什麼:

  1. 我們用 v-show="menuOpen" 來控制整個選單的顯示。
  2. 每個選單項目都是一個 nuxt-link,點擊後會跳轉到對應的頁面。
  3. exact-active-class 用來設定當前頁面的選單項目的樣式。

5. 自動關閉選單

最後,我們來加入一個功能,讓選單在頁面切換時自動關閉:

<script setup lang="ts">
// ... 前面的程式碼

watch(
  () => route.path,
  (currentPath, previousPath) => {
    if (menuOpen.value) {
      toggleMenu()
    }
  }
)
</script>

這段程式碼的作用是:

  1. 使用 watch 來監視路由的變化。
  2. 當路由改變(也就是頁面切換)時,如果選單是開啟的,就自動關閉它。

額外補充:Transition 動畫效果

為了讓我們的導航欄更加吸引人,我們可以加入一些轉場效果。在 Vue 中,我們可以使用 <Transition> 元件來實現這個功能。這個功能不只是讓網頁看起來更酷炫,還能提升使用者體驗,讓介面變化更加流暢自然。

Vue 的 Transition 元件

首先,讓我們來看看 Vue 官方文檔中關於 Transition 的說明:Vue Transition 官方文檔
Vue 提供了一個 <Transition> 元件,它可以讓我們輕鬆地為任何元素或元件添加進入/離開的動畫效果。這個元件非常強大,可以處理以下幾種情況:

  • 條件渲染 (使用 v-if)
  • 條件展示 (使用 v-show)
  • 動態元件
  • 元件根節點

基本用法

在我們的導航欄例中,我是這樣使用 Transition 的:

<template>
  <div class="relative">
    <Transition name="fade">
<!-- 選單按鈕 -->
    </Transition>

    <Transition name="fade">
<!-- 選單內容 -->
    </Transition>
  </div>
</template>

這裡的 name="fade" 是很重要的,它定義了我們的動畫效果的名稱。這個名稱會被用來生成 CSS 類別名稱,Vue 會在適當的時機添加或移除這些類別。

CSS 過渡類別

當一個元素被 <Transition> 包裹時,Vue 會在元素進入或離開時自動添加/移除一些 CSS 類別。這些類別的名稱是基於我們設定的 name 屬性。
在我們的例子中,因為我們使用了 name="fade",所以 Vue 會使用以下的類別:

  1. fade-enter-from:進入動畫的起始狀態
  2. fade-enter-active:進入動畫的生效狀態
  3. fade-enter-to:進入動畫的結束狀態
  4. fade-leave-from:離開動畫的起始狀態
  5. fade-leave-active:離開動畫的生效狀態
  6. fade-leave-to:離開動畫的結束狀態
    這些類別會在不同的動畫階段被添加和移除,讓我們能夠精確控制動畫的每個階段。

CSS 樣式定義

讓我們來看看我們為導航欄定義的 CSS 樣式:

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: all 0.4s ease-in-out;
}

.fade-enter-from,
.fade-leave-to {
  transform: translatey(-264px);
  opacity: 0;
}
</style>

這裡發生了什麼?

  1. .fade-enter-active.fade-leave-active
    • 這兩個類別定義了動畫的持續時間和變化方式。
    • transition: all 0.4s ease-in-out; 表示所有屬性的變化都會在 0.4 秒內完成,使用 ease-in-out 的變化曲線。
    • ease-in-out 曲線讓動畫在開始和結束時較慢,中間較快,創造出更自然的效果。
  2. .fade-enter-from.fade-leave-to
    • 這兩個類別定義了動畫的起始狀態和結束狀態。
    • transform: translatey(-264px); 表示元素會從上方 264px 的位置移動到原位(或從原位移動到上方 264px 的位置)。
    • opacity: 0; 表示元素會從完全透明變為可見(或從可見變為完全透明)。

動畫效果解析

結合起來,這些 CSS 樣式創造了這樣的效果:

  • 當選單出現時(進入動畫):
    • 初始狀態:選單在上方 264px 處且完全透明。
    • 動畫過程:選單在 0.4 秒內逐漸下移到原位,同時逐漸變得不透明。
    • 最終狀態:選單在原位且完全不透明。
  • 當選單消失時(離開動畫):
    • 初始狀態:選單在原位且完全不透明。
    • 動畫過程:選單在 0.4 秒內逐漸上移 264px,同時逐漸變得透明。
    • 最終狀態:選單在上方 264px 處且完全透明。

整個過程持續 0.4 秒,使用 ease-in-out 的變化曲線,這會讓動畫看起來更加平滑自然。

今日成果展示

我們得到了一個可以切換頁面又有動畫的 navbar ~
Yes

今日範例完整程式碼

  • components/Header.vue

    <script setup lang="ts">
    const route = useRoute()
    
    // 選單變數
    const menuOpen = ref(false)
    
    // 選單開關
    const toggleMenu = () => {
      menuOpen.value = !menuOpen.value
    }
    
    // 監聽路由的 path 變化, 換頁時自動關閉選單
    watch(
      () => route.path,
      (currentPath, previousPath) => {
        if (menuOpen.value) {
          toggleMenu()
        }
      }
    )
    </script>
    
    <template>
      <nuxt-link to="/" class="logo fixed left-5 top-2 w-[32px] lg:w-[40px]">
        <img src="~/assets/images/logo.svg" alt="logo" class="pic-auto" />
      </nuxt-link>
      <div class="relative">
        <!-- * 選單按鈕 -->
        <Transition name="fade">
          <button v-show="!menuOpen" @click="toggleMenu" class="nav-btn">
            <Icon name="ph:hand-tap" size="20"></Icon>
    
            <div v-if="route.path === '/'">首頁</div>
            <div v-else-if="route.path === '/calculator'">食物計算機</div>
            <div v-else-if="route.path === '/food'">營養指南</div>
            <div v-else-if="route.path === '/bird'">百科全書</div>
            <div v-else-if="route.path === '/hospital'">醫護站</div>
            <div v-else="route.path === '/connect'">聯繫我們</div>
          </button>
        </Transition>
    
        <!-- * 選單 -->
        <Transition name="fade">
          <div v-show="menuOpen" class="nav-menu">
            <button class="nav-menu-item" @click="toggleMenu">CLOSE</button>
            <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/"
              >首頁</nuxt-link
            >
            <nuxt-link
              class="nav-menu-item"
              exact-active-class="bg-blue4 bg-opacity-15"
              to="/calculator"
              >食物計算機</nuxt-link
            >
            <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/food"
              >營養指南</nuxt-link
            >
            <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/bird"
              >百科全書</nuxt-link
            >
            <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/hospital"
              >醫護站</nuxt-link
            >
            <nuxt-link class="nav-menu-item" exact-active-class="bg-blue4 bg-opacity-15" to="/connect"
              >聯繫我們</nuxt-link
            >
          </div>
        </Transition>
      </div>
    </template>
    
    <style scoped>
    .nav-btn {
      @apply bg-bg text-blue4 border-blue4 fixed right-0 top-0 z-20 flex w-[140px] items-center justify-center gap-[6px] rounded-bl-[6px] border-b border-l bg-opacity-20 p-[10px] font-bold backdrop-blur-sm;
    }
    
    .nav-menu {
      @apply text-blue4 border-blue4 fixed right-0 top-0 z-20 flex w-[140px] flex-col items-center justify-center rounded-bl-[6px] border-b border-l bg-opacity-20 font-bold backdrop-blur-sm;
    }
    
    .nav-menu-item {
      @apply flex w-full items-center justify-center p-[10px];
    }
    
    .fade-enter-active,
    .fade-leave-active {
      transition: all 0.4s ease-in-out;
    }
    
    .fade-enter-from,
    .fade-leave-to {
      transform: translatey(-264px);
      opacity: 0;
    }
    </style>
    
    
  • app.vue

    <script setup>
    import axios from 'axios'
    
    // 創建一個自定義的 Axios 實例
    const api = axios.create({
      baseURL: 'https://two024it-test-app.onrender.com' // 設置基礎 URL
      // timeout: 5000 // 設置超時時間為 5 秒
    })
    
    const list = ref([])
    
    // 測試取資料
    async function fetchData() {
      try {
        const response = await api.get('/freshfoods/') // 獲取食物列表
        console.log(response.data) // 處理響應數據
        list.value = response.data
      } catch (error) {
        console.error('發生錯誤:', error) // 錯誤處理
      }
    }
    
    onMounted(() => {
      fetchData()
    })
    </script>
    
    <template>
      <div class="app relative flex min-h-screen w-full flex-col">
        <!-- * navbar -->
        <header class="z-20">
          <Header />
        </header>
    
        <!-- * pages -->
        <main class="page-wrapper">
          <NuxtPage />
        </main>
      </div>
    </template>
    
    <style scoped>
    .app {
      font-family: 'Noto Sans TC', sans-serif;
    }
    .page-wrapper {
      @apply mx-auto flex w-full max-w-[1200px] grow px-3 pb-5 pt-[52px] lg:px-5 lg:pt-[64px];
      align-items: start;
    }
    </style>
    
    
  • nuxt.config.ts

    // https://nuxt.com/docs/api/configuration/nuxt-config
    export default defineNuxtConfig({
      devtools: { enabled: true },
      modules: ['@nuxtjs/tailwindcss', '@nuxt-icon']
    })
    
  • tailwind.config.ts

    /** @type {import('tailwindcss').Config} */
    module.exports = {
      content: [
        './components/**/*.{vue,js,ts}', // 掃描所有 Vue 組件
        './layouts/**/*.vue', // 掃描所有布局文件
        './pages/**/*.vue', // 掃描所有頁面文件
        './composables/**/*.{js,ts}', // 掃描所有可組合式函數
        './plugins/**/*.{js,ts}', // 掃描所有插件
        './utils/**/*.{js,ts}', // 掃描所有工具函數
        './{App,app}.{js,ts,vue}', // 掃描主應用文件
        './{Error,error}.{js,ts,vue}', // 掃描錯誤處理文件
        './app.config.{js,ts}' // 掃描應用配置文件
      ],
      theme: {
        extend: {
          colors: {
            blue1: '#e9f1fe',
            blue2: '#c4d7ed',
            blue3: '#abc8e2',
            blue4: '#375d81',
            blue5: '#183152',
            red1: '#ffaeaa',
            red2: '#ed6f69',
            bg: '#fff6ea'
          }
        }
      },
      plugins: [] // 可以添加 Tailwind 插件
    }
    
  • assets/css/main.css

    @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&display=swap');
    
    :root {
      --color-blue1: #e9f1fe;
      --color-blue2: #c4d7ed;
      --color-blue3: #abc8e2;
      --color-blue4: #375d81;
      --color-blue5: #183152;
    
      --color-red1: #ffaeaa;
      --color-red2: #ed6f69;
    
      --color-bg: #fff6ea;
    }
    
    body {
      font-family: 'Noto Sans TC', sans-serif;
      letter-spacing: 1.25px;
      background: var(--color-bg);
    }
    
    /* 消樣式 */
    input:focus-visible {
      outline: none;
    }
    
    textarea:focus-visible {
      outline: none;
    }
    
    select:focus-visible {
      outline: none;
    }
    
    /* 圖片通用 */
    .pic-auto {
      width: 100%;
      height: 100%;
      object-fit: cover;
      vertical-align: middle;
    }
    
    /* 限制最大寬度 !> 1200px, 搭配 px-5 使用 */
    .page-max-w {
      @apply mx-auto w-full max-w-[1200px];
    }
    
    /* hover 效果 */
    .hover-auto {
      @apply transform duration-200;
    }
    

好啦!這就是我們超酷導航欄的全部內容了。是不是覺得很有趣?雖然看起來有點複雜,但只要一步一步來,你也可以做出這麼厲害的導航欄喔!,加油!
大家有沒有想要更詳細補充的部分呢?歡迎在下方留言分享喔!讓我們一起在 Nuxt3 的世界中探險吧!加油!
(對了,如果你覺得今天的內容對你有幫助,別忘了給個讚支持一下喔!這會是我繼續努力的動力呢~)


上一篇
[ DAY24 ] Nuxt3 魔法教室:Vue 語法速成班
下一篇
[ DAY26 ] Nuxt 3 進階技巧:Pinia、API 整合、Loading 元件
系列文
房東不給養鸚鵡 - 那就用 Nuxt + Express + MongoDB 30 天寫一個全端鸚鵡專案30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言